package com.jmb.api.core.http;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.jmb.api.common.exception.LeaseException;
import com.jmb.api.common.exception.ParseException;
import com.jmb.api.common.result.RelyType;
import com.jmb.api.common.result.ResultCodeEnum;
import com.jmb.api.common.utils.*;
import com.jmb.api.core.Env;
import com.jmb.api.core.NodeRequest;
import com.jmb.api.core.parser.Parser;
import com.jmb.api.model.entity.*;
import com.jmb.api.model.vo.api.ApiTestAssertVO;
import com.jmb.api.model.vo.api.ApiTestCaseInfoVo;
import com.jmb.api.model.vo.api.log.ApiTestCaseExecuteLogVO;
import com.jmb.api.service.*;
import com.jmb.api.vo.api.suite.ApiCaseSuiteVo;
import com.jmb.api.vo.project.ProjectVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import static com.alibaba.fastjson2.JSON.parseArray;

@Slf4j
@Component
public class ExecuteHandler implements NodeRequest {

    @Autowired
    private ApiTestCaseService apiTestCaseService;
    @Autowired
    private ApiCaseSuiteService caseSuiteService;
    @Autowired
    private ProjectService projectService;
    @Autowired
    private ApiTestPreCaseService preCaseService;
    @Autowired
    private Env env;
    @Autowired
    private ApiTestCaseExecuteLogService executeLogService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private Parser parser;
    @Autowired
    private ApiTestCacheParameterLogService  cacheParameterLogService;
    @Autowired
    private ApiTestCaseAssertLogService assertLogService;

    /**
     * 执行指定接口测试用例
     * @param ex 入参参数
     * @return 自增的日志编号
     */
    public Integer executeApiTestCase(ExecuteInterfaceCaseParam ex) throws ParseException {

        // 获取类型
        Integer interfaceCaseId = ex.getInterfaceCaseId();
        String executor = ex.getExecutor();
        String suiteLogNo = ex.getSuiteLogNo();
        String chainNo = ex.getChainNo();
        Integer suiteId = ex.getSuiteId();
        Integer isFailedRetry = ex.getIsFailedRetry();
        String suiteLogDetailNo = ex.getSuiteLogDetailNo();
        HashMap globalHeaders = ex.getGlobalHeaders();
        HashMap globalParams = ex.getGlobalParams();
        HashMap globalData = ex.getGlobalData();
        Integer source = ex.getSource();
        String casePreNo = ex.getCasePreNo();
        Boolean skipPreCase = ex.getSkipPreCase();

        // 公共变量
        String url;
        // 接受异常信息
        String exceptionMessage = null;
        // 运行结果 0成功 1失败 2 错误
        // 默认为0
        int caseStatus = 0;
        // 记录请求日志requestBody字段
        String requestBody = null;
        // 记录开始时间
        long methodStart = TimeUtil.now();
        // 获取case详情
        ApiTestCaseInfoVo testCaseInfoVo = apiTestCaseService.findInterfaceCaseByCaseId(ex.getInterfaceCaseId());

        if (testCaseInfoVo == null){
            throw new LeaseException(ResultCodeEnum.WEB_API_DATA_NOT_EXIST);
        }
        String description = testCaseInfoVo.getDescription();
        Integer projectId = testCaseInfoVo.getProjectId();
        String caseInfoVoHeaders = testCaseInfoVo.getHeaders();
        log.info("获取到的headers{}",caseInfoVoHeaders);
        //  source 来源（0用例调试 1依赖调试 2运行整个测试套件 3测试套件单个用例调试 4依赖解析 5综合用例-前置用例）
        // 这个地方做了简化处理
        RelyType startRelyType = switch (ex.getSource()){
            case 1, 4 -> RelyType.RELY_START;
            case 5 -> RelyType.PRE_CASE_START;
            default -> RelyType.CASE_START;
        };
        RelyType endRelyType = switch (ex.getSource()) {
            case 1, 4 -> RelyType.RELY_END;
            case 5 -> RelyType.PRE_CASE_END;
            default -> RelyType.CASE_END;
        };
        log.info("--------------------------------任务接口 [{}] 开始 执行  时间:{} --------------------------------", ex.getInterfaceCaseId(),TimeUtil.now()-methodStart);
        // 压栈
        redisUtil.stackPush(ex.getChainNo(),chainNode(startRelyType, null,testCaseInfoVo.getDescription(),null,TimeUtil.now()-methodStart,null));
        // 判断运行环境
        if (testCaseInfoVo.getProjectId() !=null){
            ProjectVo project = projectService.getProjectById(testCaseInfoVo.getProjectId());
            //判断运行环境
            if (ex.getSuiteId() == null){
                // 如果没有传suiteId，则直接使用调试环境
                url = env.domain(project,4) + testCaseInfoVo.getUrl();
            }else {
                // 如果有suiteId，则使用suiteId对应的运行环境
                ApiCaseSuiteVo suiteVo = caseSuiteService.getApiCaseSuiteList(ex.getSuiteId());
                url = env.domain(project,suiteVo.getRunDev()) + testCaseInfoVo.getUrl();
            }
        }else {
            // 如果没有projectId，则不使用host port protocol 拼接url
            url = testCaseInfoVo.getUrl();
        }
        // 过滤未启用的属性
        String headers = kvCast(testCaseInfoVo.getHeaders());
        String params = kvCast(testCaseInfoVo.getParams());
        String formData = kvCast(testCaseInfoVo.getFormData());
        String formDataEncoded = kvCast(testCaseInfoVo.getFormDataEncoded());
        String rawHeaders = headers;
        String rawParams = params;
        String raw = testCaseInfoVo.getRaw();
        String rawType = testCaseInfoVo.getRawType();
        Integer bodyType = testCaseInfoVo.getBodyType();
        Integer method = testCaseInfoVo.getMethod();
        String rawBody = this.logRawBody(bodyType, formData, formDataEncoded, raw);

        // 日志记录最终的rawBody
        // 开始时间
        long startTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        log.info("--------------------------------任务接口 [{}] 开始 执行前置用例--------------------------------", ex.getInterfaceCaseId());
        // 执行前置用例
        if (!ex.getSkipPreCase()){
           List<ApiTestPreCase> preCaseList = preCaseService.getPreCaseId(ex.getInterfaceCaseId());
           if (preCaseList !=null && !preCaseList.isEmpty()){
               // 前置用例编号
               String no = NoUtil.genCasePreNo();
               casePreNo = no;
               for (ApiTestPreCase preCase : preCaseList){
                   Integer caseId = preCase.getPreCaseId();
                   log.info("开始执行：[{}] 前置用用例.......",caseId);
                   // 设置前置用例的source为5
                   ex.setSource(5);
                   ex.setCasePreNo(no);
                   ex.setExecutor("前置用例");
                   // 把前置用例作为中间依赖case处理
                   ex.setSuiteLogNo(null);
                   ex.setInterfaceCaseId(caseId);
                   // 当用例作为前置用例执行时，应该跳过前置用例的前置用例，仅执行自身
                   ex.setSkipPreCase(true);
                   Integer logId = this.executeApiTestCase(ex);
                   // 查询用例执行状态
                   // 获取到日志log对象
                   ApiTestCaseExecuteLogVO logg = executeLogService.findExecute(logId);
                   // 获取到用例描述
                   String DescCase = logg.getCaseDesc();
                   // 获取到用例状态
                   Integer status = logg.getStatus();
                   if (status !=0){
                       if (status == 1){
                           // status 1 是失败
                           // 获取失败信息赋值给message
                           exceptionMessage= String.format("前置用例 [%s] [%s] 执行失败：错误日志是:[%s]" + "其他用例停止运行", caseId, DescCase, logg.getErrorMessage());
                       }else {
                           exceptionMessage = String.format("前置用例 [%s] [%s] 执行错误：错误日志是:[%s]" + "其他用例停止运行", caseId, DescCase, logg.getErrorMessage());
                       }
                       caseStatus = 2;
                       log.error( exceptionMessage);
                       break; // 终止循环，其他前置用例不再执行
                   }
                    log.info("前置用例 [{}] 执行结束", caseId);
               }
           }
        }
        // 当前前置用例执行失败，记录日志并停止执行(用例请求失败时触发（此时还未执行断言）)
        if (caseStatus == 2) {
            // 结束时间
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() - startTime;
            log.info("前置用例执行失败，停止后续流程...........");
            return this.onRequestError(ex.getInterfaceCaseId(), url, description, method, null, null, null,
                    rawHeaders, rawParams, rawBody, ex.getExecutor(), exceptionMessage, endTime,
                    ex.getSuiteLogNo(), ex.getSuiteLogDetailNo(),ex.getIsFailedRetry(), ex.getSource(), rawType, bodyType, ex.getChainNo());
        }
        // 执行接口，发送请求
        ResponseEntity responseEntity  = null;
        HashMap headersMap = null;
        HashMap paramsMap = null;
        HashMap formDataMap;
        HashMap formDataEncodedMap;
        try {
            // 1. 解析Headers中依赖
            headers = parser.parseDependency( headers, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
            // 2. 解析params中依赖
            params = parser.parseDependency( params, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
            if (bodyType == 0){ // form-data
                formData = parser.parseDependency( formData, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
            } else  if (bodyType == 1){ // //x-www-form-encoded
                formDataEncoded = parser.parseDependency( formDataEncoded, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
            } else  if(bodyType == 2){ // raw
                raw = parser.parseDependency( raw, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
            }else if (bodyType == 9) { //noe
                log.info("rawType为9，不执行请求");
            }
            else {
                throw new LeaseException(ResultCodeEnum.BODY_TYPE_NOT_ERRORO);}
            // 合并公共的Headers
            this.mergeSuiteProperty(globalHeaders,headers);
            // 合并公共Params
            this.mergeSuiteProperty(globalParams,params);
            // 合并公共formDataEncoded
            formDataEncodedMap = this.mergeSuiteProperty(globalData, formDataEncoded);
            // formData 不合并
            formDataMap = JSONObject.parseObject(formData, HashMap.class);
            // 处理URL参数
            HashMap<String,Object> urlParamsWrapper = Request.pathVariableParser(url, paramsMap);
            // 提取url赋值给Url
            url = (String) urlParamsWrapper.get("url");
            // 提取params赋值给paramsMap
            paramsMap = (HashMap<String, String>) urlParamsWrapper.get("params");
            // 确定日志记录最终记录的requestBody
            requestBody = this.logRequestBody(bodyType, formDataMap, formDataEncodedMap, raw);
            // 确定请求方式
            HttpMethod methodEnum = this.httpMethod(method);
            // 发送接口请求
            responseEntity = this.doRequest(bodyType, methodEnum, url, headersMap, paramsMap, formDataMap, formDataEncodedMap, raw, rawType);
        }
        catch (ResourceAccessException e){
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() - startTime;
             // 赋值status状态为错误
            caseStatus = 2;
            log.error("连接超时，请检查超时设置和代理服务器.产生的错误日志信息:",e);
            log.error("接口请求耗时: {} ms", endTime);
            exceptionMessage = "连接超时，请检查超时设置和代理服务器";
        }catch (Exception e){
            caseStatus = 2;
            // 把错误日志信息转成字符串打印
            log.error(ExceptionUtil.msg(e));
            exceptionMessage = ExceptionUtil.msg(e);
        }
        Integer responseCode;
        String responseHeaders;
        String responseBody;

        if (responseEntity == null){
            // 请求出错
            // 请求接口出错记录时间
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() - startTime;
            log.info("接口请求耗时: {} ms", endTime);
            log.error("接口请求耗时: {} ms", endTime);
            return this.onRequestError(interfaceCaseId, url, description, method, headersMap, paramsMap, requestBody,
                    rawHeaders, rawParams, rawBody, executor, exceptionMessage, endTime,
                    suiteLogNo, suiteLogDetailNo, isFailedRetry, source, rawType, bodyType, chainNo);
        }else {
            // 请求成功的处理
            responseCode = Request.code(responseEntity);
            responseHeaders = Request.headersPretty(responseEntity);
            responseBody = Request.body(responseEntity);
            // 请求日志记录
            long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() - startTime;
            int executedLogId = this.onRequestSuccess(responseCode, responseHeaders, responseBody,
                    interfaceCaseId, url, description, method, headersMap, paramsMap, requestBody,
                    rawHeaders, rawParams, rawBody, endTime, executor, suiteLogNo, suiteLogDetailNo,
                    isFailedRetry, source, rawType, bodyType, caseStatus);

            // 请求参数缓存
            this.cacheRequest(testCaseInfoVo.getPostProcessors(), executedLogId, headers, params, formDataEncoded,
                    raw, casePreNo, suiteLogDetailNo, chainNo);
            // 执行断言
            List<ApiTestAssertVO> asserts = testCaseInfoVo.getAsserts();
            Integer statusAfterAssert = this.doAssert(asserts, interfaceCaseId, executedLogId,
                    responseCode, responseHeaders, responseBody,endTime,
                    chainNo, suiteId, isFailedRetry, suiteLogDetailNo, casePreNo,
                    globalHeaders, globalParams, globalData);
            // 响应数据缓存
            this.cacheResponse(testCaseInfoVo.getPostProcessors(),interfaceCaseId,executedLogId,statusAfterAssert,
                    responseHeaders,responseBody,casePreNo,suiteLogDetailNo, chainNo);
            // 手动写入，否则前置用例跟踪链看不到
            redisUtil.stackPush(chainNo, chainNode(endRelyType, executedLogId, testCaseInfoVo.getDescription(),
                    null, TimeUtil.now()-methodStart, null));
            ApiTestCaseExecuteLog modifyChain = new ApiTestCaseExecuteLog();
            modifyChain.setRunTime(endTime);
            modifyChain.setId(executedLogId);
            modifyChain.setChain(JSON.toJSONString(redisUtil.stackGetAll( chainNo)));
            executeLogService.modifyExecuteLog( modifyChain);
            // 返回我们的日志编号
            return executedLogId;
        }

    }

    /**
     * 缓存响应数据
     * @param processorList 处理器列表
     * @param interfaceCaseId 测试用例编号
     * @param executedLogId 此次执行日志编号
     * @param statusAfterAssert 断言处理后的执行日志状态
     * @param responseHeaders 响应头
     * @param responseBody 响应体
     * @param casePreNo 前置用例的参数缓存用的key，为了防止异步执行用例时，tempPostProcessor key被覆盖， 仅前置用例执行时，需要该参数
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param chainNo 调用链路跟踪 将调用链信息序列
     */
    private void cacheResponse(List<ApiTestCacheParameter> processorList, Integer interfaceCaseId, Integer executedLogId,
                               Integer statusAfterAssert, String responseHeaders, String responseBody,
                               String casePreNo, String suiteLogDetailNo, String chainNo){
        if (statusAfterAssert.intValue() == 0 ) { // *仅用例执行通过才写入处理器记录

            ApiTestCacheParameterLog interfaceProcessorLogDO;
            String value = null;

            if (!processorList.isEmpty()) {
                for(ApiTestCacheParameter processorDO : processorList) {
                    Integer type = processorDO.getType();
                    String expression = processorDO.getExpression();
                    String name = processorDO.getName();
                    Integer haveDefaultValue = processorDO.getHaveDefaultValue();
                    String defaultValue = processorDO.getDefaultValue();
                    Integer isDefaultValue = null;
                    Integer status = 0; // 状态 0通过 1失败
                    String errorMsg = "";
                    boolean isThrowException = false;
                    String exceptionMsg = "";
                    if (type == 0 ) { // json
                        String s = "[]";
                        try {
                            s = ParseUtil.parseJson(responseBody, expression);
                        } catch (ParseException e) {
                            isThrowException = true;
                            exceptionMsg = e.getMessage();
                        }
                        ArrayList jsonPathArray = JSONObject.parseObject(s, ArrayList.class);
                        if (jsonPathArray.isEmpty()) {
                            if (haveDefaultValue == 0) {
                                isDefaultValue = 0;
                                value = defaultValue;
                                log.info("提取类型={}，提取表达式={} 为空，使用默认值={}", "response-json", expression, value);
                            } else {
                                status = 1;
                                errorMsg = String.format("response cache error, json-path extract content is null, " +
                                        "expression[%s], processor=[%s], case=[%s]", expression, name, interfaceCaseId);
                                log.error(errorMsg);
                            }
                        } else {
                            isDefaultValue = 1;
                            if (jsonPathArray.size() == 1) {
                                Object o = jsonPathArray.get(0);
                                value = o == null ? null : o.toString();
                            } else {
                                value = JSON.toJSONString(jsonPathArray);
                            }
                            log.info("提取类型={}，提取表达式={}，写入缓存内容={}", "response-json", expression, value);
                        }
                    } else if (type == 1) { // xml
                        String s = "[]";
                        try {
                            s = ParseUtil.parseXml(responseBody, expression);
                        } catch (ParseException e) {
                            isThrowException = true;
                            exceptionMsg = e.getMessage();
                        }
                        ArrayList xpathArray = JSONObject.parseObject(s, ArrayList.class);
                        if (xpathArray.isEmpty()) {
                            if (haveDefaultValue == 0) {
                                isDefaultValue = 0;
                                value = defaultValue;
                                log.info("提取类型={}，提取表达式={} 为空，使用默认值={}", "response-xml", expression, value);
                            } else {
                                status = 1;
                                errorMsg = String.format("response cache error, xpath extract content is null, " +
                                        "expression[%s], processor=[%s], case=[%s]", expression, name, interfaceCaseId);
                                log.error(errorMsg);
                            }
                        } else {
                            isDefaultValue = 1;
                            if (xpathArray.size() == 1) {
                                Object o = xpathArray.get(0);
                                value = o == null ? null : o.toString();
                            } else {
                                value = JSON.toJSONString(xpathArray);
                            }
                            log.info("提取类型={}，提取表达式={}，写入缓存内容={}", "response-xml", expression, value);
                        }
                    } else if (type == 2) { //header
                        String s = "[]";
                        try {
                            s = ParseUtil.parseJson(responseHeaders, expression);
                        } catch (ParseException e) {
                            isThrowException = true;
                            exceptionMsg = e.getMessage();
                        }
                        ArrayList headerArray = JSONObject.parseObject(s, ArrayList.class);
                        if (headerArray == null || headerArray.isEmpty()) {
                            if (haveDefaultValue == 0) {
                                isDefaultValue = 0;
                                value = defaultValue;
                                log.info("提取类型={}，提取表达式={} 为空，使用默认值={}", "header", expression, value);
                            } else {
                                status = 1;
                                errorMsg = String.format("response cache error, headers json-path extract content is null, " +
                                        "expression[%s], processor=[%s], case=[%s]", expression, name, interfaceCaseId);
                                log.error(errorMsg);
                            }
                        } else {
                            isDefaultValue = 1;
                            if (headerArray.size() == 1) {
                                Object o = headerArray.get(0);
                                value = o == null ? null : o.toString();
                                JSONArray var1 = parseArray(value);
                                if (var1 == null) {
                                    value = null;
                                } else {
                                    if (var1.size() == 1) {
                                        Object var2 = var1.get(0);
                                        value = var2 == null ? null : var2.toString();
                                    } else {
                                        value = JSON.toJSONString(var1);
                                    }
                                }
                            } else {
                                value = JSON.toJSONString(headerArray);
                            }
                        }
                    } else if (type >= 7) {
                        status = 1;
                        log.error("不支持该处理器类型");
                        errorMsg = "不支持该处理器类型";
                    }

                    if (type <= 2) {
                        interfaceProcessorLogDO = new ApiTestCacheParameterLog();
                        interfaceProcessorLogDO.setProcessorId(processorDO.getProcessorId());
                        interfaceProcessorLogDO.setCaseId(processorDO.getCaseId());
                        interfaceProcessorLogDO.setCaseExecuteLogId(executedLogId);
                        interfaceProcessorLogDO.setName(name);
                        interfaceProcessorLogDO.setType(type);
                        interfaceProcessorLogDO.setExpression(expression);
                        if (!isThrowException) {
                            // 写入处理器记录表
                            interfaceProcessorLogDO.setIsDefaultValue(isDefaultValue);
                            interfaceProcessorLogDO.setValue(value);
                            interfaceProcessorLogDO.setCreateTime(new Date());
                            interfaceProcessorLogDO.setStatus(status);
                            interfaceProcessorLogDO.setWr(1);
                            if (status == 0) {
                                interfaceProcessorLogDO.setErrorMsg(null);
                            } else {
                                interfaceProcessorLogDO.setErrorMsg(errorMsg);
                            }
                            // 写入处理器日志表
                            long start = TimeUtil.now();
                            cacheParameterLogService.saveInterfaceProcessorLog(interfaceProcessorLogDO);
                            RelyType relyType; // 0response-json/1response-html/2response-header
                            if (type == 0) {
                                relyType = RelyType.WRITE_PROCESSOR_RESPONSE_JSON;
                            } else if (type == 1) {
                                relyType = RelyType.WRITE_PROCESSOR_RESPONSE_HTML;
                            } else {
                                relyType = RelyType.WRITE_PROCESSOR_RESPONSE_HEADER;
                            }
                            log.info("写入处理器日志表");
                            // 如果处理器出错，那么将用例执行状态重新置为错误
                            if (status == 1) {
                                ApiTestCaseExecuteLog afterPostProcessorLogDo = new ApiTestCaseExecuteLog();
                                afterPostProcessorLogDo.setId(executedLogId);
                                afterPostProcessorLogDo.setStatus(2);
                                afterPostProcessorLogDo.setErrorMessage(errorMsg);
                                executeLogService.modifyExecuteLog(afterPostProcessorLogDo);
                                log.info("处理器运行错误，主动将用例执行状态置为错误");
                            } else {
                                if (casePreNo != null) { //如果为前置用例，仅写入前置用例域，
                                    redisUtil.hashPut(casePreNo, name, value);
                                    log.info("写入处理器-前置用例域，key={}，hashKey={}，value={}", casePreNo, name, value);
                                } else {
                                    if (suiteLogDetailNo == null) { // 不存在则写入临时域
                                        redisUtil.hashPut(NoUtil.TEMP_POST_PROCESSOR_NO, name, value);
                                        log.info("写入处理器-临时域，key={}，hashKey={}，value={}", NoUtil.TEMP_POST_PROCESSOR_NO, name, value);
                                    } else { // 否则写入测试套件域
                                        redisUtil.hashPut(suiteLogDetailNo, name, value, 24*60*60);
                                        log.info("写入处理器-测试套件域，key={}，hashKey={}，value={}", suiteLogDetailNo, name, value);
                                    }
                                }
                            }
                            redisUtil.stackPush(chainNo, chainNode(relyType, null, name, value, TimeUtil.now()-start, expression));
                            // 手动写入，否则前置用例的跟踪链看不到
                            ApiTestCaseExecuteLog modifyChain = new ApiTestCaseExecuteLog();
                            modifyChain.setId(executedLogId);
                            modifyChain.setChain(JSON.toJSONString(redisUtil.stackGetAll(chainNo)));
                            executeLogService.modifyExecuteLog(modifyChain);
                        } else { // 若解析json、xpath出错
                            // 写入处理器记录表
                            interfaceProcessorLogDO.setIsDefaultValue(null);
                            interfaceProcessorLogDO.setValue(null);
                            interfaceProcessorLogDO.setCreateTime(new Date());
                            interfaceProcessorLogDO.setStatus(1);
                            interfaceProcessorLogDO.setErrorMsg(exceptionMsg);
                            cacheParameterLogService.saveInterfaceProcessorLog(interfaceProcessorLogDO);
                            log.error("处理器解析出错，写入处理器记录表");
                            // 重置用例状态
                            ApiTestCaseExecuteLog afterPostProcessorLogDo = new ApiTestCaseExecuteLog();
                            afterPostProcessorLogDo.setId(executedLogId);
                            afterPostProcessorLogDo.setStatus(2);
                            afterPostProcessorLogDo.setErrorMessage(exceptionMsg);
                            executeLogService.modifyExecuteLog(afterPostProcessorLogDo);
                            log.info("处理器解析出错，主动将用例执行状态置为错误");
                        }
                    }
                }
            }
        }
    }

    /**
     * 将数据库中的method字段映射HttpMethod枚举
     * @param methodInteger 数据库中的method字段
     * @return HttpMethod枚举
     */
    private HttpMethod httpMethod(Integer methodInteger) {
        if (methodInteger == 0) {
            return HttpMethod.GET;
        } else if (methodInteger == 1) {
            return HttpMethod.POST;
        } else if (methodInteger == 2) {
            return HttpMethod.PATCH;
        } else if (methodInteger == 3) {
            return HttpMethod.PUT;
        } else if (methodInteger == 4) {
            return HttpMethod.DELETE;
        } else if (methodInteger == 5) {
            return HttpMethod.HEAD;
        } else if (methodInteger == 6) {
            return HttpMethod.OPTIONS;
        } else if (methodInteger == 7) {
            return HttpMethod.TRACE;
        } else {
            log.error("不支持该http方法");
            throw new LeaseException(ResultCodeEnum.WEB_API_HTTP_NOT);
        }
    }

    private String logRequestBody(Integer bodyType, HashMap formDataMap, HashMap formDataEncodedMap, String raw) {
        if (bodyType == 0) { //form-data
            return JSON.toJSONString(formDataMap, JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(formDataMap, JSONWriter.Feature.PrettyFormat);
        } else if (bodyType == 1) { //x-www-form-encoded
            return JSON.toJSONString(formDataEncodedMap, JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(formDataEncodedMap, JSONWriter.Feature.PrettyFormat);
        } else if (bodyType == 2) { //raw
            return raw;
        } else if (bodyType == 9) { //none
            return null;
        } else {
            throw new LeaseException( ResultCodeEnum.BODY_TYPE_NOT_ERRORO);
        }
    }

    /**
     * 请求参数缓存
     * @param processorList 处理器列表
     * @param executedLogId 执行日志编号
     * @param headers 请求headers
     * @param params 请求params
     * @param formDataEncoded 请求formDataEncoded
     * @param raw 请求raw
     * @param casePreNo 前置用例的参数缓存用的key，为了防止异步执行用例时，tempPostProcessor key被覆盖， 仅前置用例执行时，需要该参数
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param chainNo 调用链路跟踪 将调用链信息序列化
     */
    private void cacheRequest(List<ApiTestCacheParameter> processorList, Integer executedLogId,
                              String headers, String params, String formDataEncoded, String raw,
                              String casePreNo, String suiteLogDetailNo, String chainNo) throws ParseException {

        // 查询用例的请求参数
        ApiTestCacheParameterLog apiTestCacheParameterLog;
        String value = null; // 缓存值

        if (!processorList.isEmpty()) {
            for (ApiTestCacheParameter processorDO : processorList) {
                Integer type = processorDO.getType();
                String expression = processorDO.getExpression();
                String name = processorDO.getName();
                Integer haveDefaultValue = processorDO.getHaveDefaultValue();
                String defaultValue = processorDO.getDefaultValue();
                Integer caseId = processorDO.getCaseId();

                Integer isDefaultValue = null;
                Integer status = 0; // 状态 0通过 1失败
                String errorMsg = "";
                boolean isThrowException = false;
                String exceptionMsg = "";

                if (type >= 3 && type <= 6) { // 3request-header/4request-params/5request-data/6request-json
                    String s = "[]";
                    try {
                        switch (type) {
                            case 3:
                                s = ParseUtil.parseJson(headers, expression);
                                break;
                            case 4:
                                s = ParseUtil.parseJson(params, expression);
                                break;
                            case 5:
                                s = ParseUtil.parseJson(formDataEncoded, expression);
                                break;
                            case 6:
                                s = ParseUtil.parseJson(raw, expression);
                                break;
                        }
                    } catch (ParseException e) {
                        isThrowException = true;
                        exceptionMsg = e.getMessage();
                        errorMsg = e.getMessage();
                    }
                    ArrayList jsonPathArray = JSONObject.parseObject(s, ArrayList.class);
                    if (jsonPathArray.isEmpty()) {
                        if (haveDefaultValue == 0) {
                            isDefaultValue = 0;
                            value = defaultValue;
                        } else {
                            status = 1;
                            errorMsg = String.format("请求缓存错误, json-path 提取内容为空, " +
                                    "提取表达式[%s], processor=[%s], case=[%s]", expression, name, caseId);
                            log.error(errorMsg);
                        }
                    } else {
                        isDefaultValue = 1;
                        if (jsonPathArray.size() == 1) {
                            Object o = jsonPathArray.get(0);
                            value = o == null ? null : o.toString();
                        } else {
                            value = JSON.toJSONString(jsonPathArray);
                        }
                    }
                    apiTestCacheParameterLog = new ApiTestCacheParameterLog();
                    apiTestCacheParameterLog.setProcessorId(processorDO.getProcessorId());
                    apiTestCacheParameterLog.setCaseId(processorDO.getCaseId());
                    apiTestCacheParameterLog.setCaseExecuteLogId(executedLogId);
                    apiTestCacheParameterLog.setName(name);
                    apiTestCacheParameterLog.setType(type);
                    apiTestCacheParameterLog.setExpression(expression);
                    apiTestCacheParameterLog.setWr( 1);
                    if (!isThrowException) {
                        apiTestCacheParameterLog.setIsDefaultValue(isDefaultValue);
                        apiTestCacheParameterLog.setValue(value);
                        apiTestCacheParameterLog.setCreateTime(new Date());
                        apiTestCacheParameterLog.setStatus(status);
                        if (status == 0) {
                            apiTestCacheParameterLog.setErrorMsg(null);
                        } else {
                            apiTestCacheParameterLog.setErrorMsg(errorMsg);
                        }
                        long start = TimeUtil.now();
                        cacheParameterLogService.saveInterfaceProcessorLog(apiTestCacheParameterLog);
                        // type=3request-header/4request-params/5request-data/6request-json的处理器
                        RelyType relyType;
                        if (type == 3) {
                            relyType = RelyType.WRITE_PROCESSOR_REQUEST_HEADER;
                        } else if (type == 4) {
                            relyType = RelyType.WRITE_PROCESSOR_REQUEST_PARAMS;
                        } else if (type == 5) {
                            relyType = RelyType.WRITE_PROCESSOR_REQUEST_DATA;
                        } else {
                            relyType = RelyType.WRITE_PROCESSOR_REQUEST_JSON;
                        }
                        if (status == 1) {
                            ApiTestCaseExecuteLog afterPostProcessorLogDo = new ApiTestCaseExecuteLog();
                            afterPostProcessorLogDo.setId(executedLogId);
                            afterPostProcessorLogDo.setStatus( 2);
                            afterPostProcessorLogDo.setErrorMessage(errorMsg);
                            executeLogService.modifyExecuteLog(afterPostProcessorLogDo);
                            log.info("处理器运行错误，主动将用例执行状态置为错误");
                            throw new ParseException(errorMsg);
                        } else {
                            if (casePreNo != null) { //如果为前置用例，仅写入前置用例域，
                                redisUtil.hashPut(casePreNo, name, value);
                                log.info("写入处理器-前置用例域，key={}，hashKey={}，value={}", casePreNo, name, value);
                            } else {
                                if (suiteLogDetailNo == null) { // 不存在则写入临时域
                                    redisUtil.hashPut(NoUtil.TEMP_POST_PROCESSOR_NO, name, value);
                                    log.info("写入处理器-临时域，key={}，hashKey={}，value={}", NoUtil.TEMP_POST_PROCESSOR_NO, name, value);
                                } else { // 否则写入测试套件域
                                    redisUtil.hashPut(suiteLogDetailNo, name, value, 24*60*60);
                                    log.info("写入处理器-测试套件域，key={}，hashKey={}，value={}", suiteLogDetailNo, name, value);
                                }
                            }
                        }
                        redisUtil.stackPush(chainNo, chainNode(relyType, null, name, value, TimeUtil.now()-start, expression));
                        // 手动写入，否则前置用例的跟踪链看不到
                        ApiTestCaseExecuteLog modifyChain = new ApiTestCaseExecuteLog();
                        modifyChain.setId(executedLogId);
                        modifyChain.setChain(JSON.toJSONString(redisUtil.stackGetAll(chainNo)));
                        executeLogService.modifyExecuteLog(modifyChain);
                    } else { // 若解析json、xpath出错
                        // 写入处理器记录表
                        apiTestCacheParameterLog.setIsDefaultValue(null);
                        apiTestCacheParameterLog.setValue(null);
                        apiTestCacheParameterLog.setCreateTime(new Date());
                        apiTestCacheParameterLog.setStatus( 1);
                        apiTestCacheParameterLog.setErrorMsg(exceptionMsg);
                        cacheParameterLogService.saveInterfaceProcessorLog(apiTestCacheParameterLog);
                        log.error("处理器解析出错，写入处理器记录表");
                        // 重置用例状态
                        ApiTestCaseExecuteLog apiTestCaseExecuteLog = new ApiTestCaseExecuteLog();
                        apiTestCaseExecuteLog.setId(executedLogId);
                        apiTestCaseExecuteLog.setStatus(2);

                        apiTestCaseExecuteLog.setErrorMessage(exceptionMsg);
                        executeLogService.modifyExecuteLog(apiTestCaseExecuteLog);
                        log.error("处理器解析出错，主动将用例执行状态置为错误");
                        throw new ParseException(errorMsg);
                    }
                }
            }
        }
    }


    /**
     * 执行用例的全部断言
     * @param asserts 断言列表
     * @param interfaceCaseId 测试用例编号
     * @param executedLogId 此次执行日志编号
     * @param responseCode 响应状态码
     * @param responseHeaders 响应头
     * @param responseBody 响应体
     * @param runTime 执行耗时
     * @param chainNo 调用链路跟踪 将调用链信息序列化
     * @param suiteId 测试套件编号
     * @param isFailedRetry 记录此次是否为失败重跑
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param casePreNo 前置用例的参数缓存用的key，为了防止异步执行用例时，tempPostProcessor key被覆盖， 仅前置用例执行时，需要该参数
     * @param globalHeaders 测试套件的headers
     * @param globalParams 测试套件的params
     * @param globalData 测试套件的data
     * @return 根据断言列表中的每次断言结果判定最终断言结果并返回
     */
    private Integer doAssert(List<ApiTestAssertVO> asserts, Integer interfaceCaseId, Integer executedLogId,
                             Integer responseCode, String responseHeaders, String responseBody, long runTime,
                             String chainNo, Integer suiteId, Integer isFailedRetry, String suiteLogDetailNo, String casePreNo,
                             HashMap globalHeaders, HashMap globalParams, HashMap globalData) throws ParseException {

        List<Integer> statusList = new ArrayList<>(); // 用来接收用例每个断言的结果

        for (ApiTestAssertVO interfaceAssertVO : asserts) {
            Integer assertStatus; // 是否通过 0通过 1失败 2错误
            String assertErrorMessage = null;

            Integer assertId = interfaceAssertVO.getAssertId();
            String assertName = interfaceAssertVO.getAssertName();
            Integer type = interfaceAssertVO.getType();
            String expression = interfaceAssertVO.getExpression();
            Integer operator = interfaceAssertVO.getOperator();
            Integer order = interfaceAssertVO.getOrderId();

            // 断言基本信息
            ApiTestCaseAssertLog assertLogDO = new ApiTestCaseAssertLog();
            assertLogDO.setExecuteLogId(executedLogId);
            assertLogDO.setCaseId(interfaceCaseId);
            assertLogDO.setAssertName(assertName);
            assertLogDO.setAssertId(assertId);
            assertLogDO.setType(type);
            assertLogDO.setExpression(expression);
            assertLogDO.setOperator(operator);
            String exceptedResult = interfaceAssertVO.getExceptedResult();
            assertLogDO.setRawExceptedResult(exceptedResult);

            // 当断言出错
            if (null != exceptedResult) {
                try {
                    log.info("start parsing assertion expected results");
                    exceptedResult = parser.parseDependency(exceptedResult, chainNo, suiteId, isFailedRetry, suiteLogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
                    log.info("完成对预期结果的分析、预期结果。 [{}]", exceptedResult);
                } catch (  LeaseException  e) {
                    assertErrorMessage = e.getMessage();
                    assertStatus = 2;
                    statusList.add(assertStatus); // 将每次断言status都加入集合
                    assertLogDO.setExceptedResult(exceptedResult);
                    assertLogDO.setOrder(order);
                    assertLogDO.setActualResult(null);
                    assertLogDO.setStatus(assertStatus);
                    assertLogDO.setErrorMessage(assertErrorMessage);
                    assertLogDO.setCreatedTime(new Date());
                    log.error("解析断言的预期结果进程时出错");
                    assertLogService.saveAssertLog(assertLogDO);
                    continue;
                }
            }

            // 执行断言
            assertLogDO.setExceptedResult(exceptedResult);
            assertLogDO.setOrder(order);
            String actualResult = null;
            ArrayList resultList = null;
            boolean isPass;
            try {
                if (type == 0) { // json
                    actualResult = ParseUtil.parseJson(responseBody, expression);
                    log.info("实际 results={}, assert type={}, body={}, expression={}", actualResult, "json-path", responseBody, expression);
                    resultList = JSONObject.parseObject(actualResult, ArrayList.class);
                } else if (type == 1) { // html
                    actualResult = ParseUtil.parseXml(responseBody, expression);
                    log.info("实际 results={}, assert type={}, body={}, expression={}", actualResult, "xpath", responseBody, expression);
                    resultList = JSONObject.parseObject(actualResult, ArrayList.class);
                } else if (type == 2) { // header
                    actualResult = ParseUtil.parseJson(responseHeaders, expression);
                    log.info("实际 results={}, assert type={}, header={}, expression={}", actualResult, "json-path", responseHeaders, expression);
                    resultList = JSONObject.parseObject(actualResult, ArrayList.class);
                } else if (type == 3) { // responseCode
                    actualResult = String.valueOf(responseCode);
                    log.info("实际 results={}, assert type={}, code={}, expression={}", actualResult, "code", responseCode, expression);
                } else if (type == 4) { // runtime
                    actualResult = String.valueOf(runTime);
                    log.info("实际 results={}, assert type={}, runtime={}, expression={}", actualResult, "runtime", actualResult, expression);
                } else {
                    throw new LeaseException(ResultCodeEnum.WEB_API_ASSERT_NOT);
                }
                log.info("预期结果={}", exceptedResult);
                log.info("操作符={}", operator);
                log.info("实际结果={}", actualResult);

                if (type < 3) { // 0json/1html/2header/3responseCode/4runtime
                    if (resultList.size() == 1) {
                        Object o = resultList.get(0);
                        actualResult = o == null ? null : o.toString();
                        if (type == 2) {
                            JSONArray var1 = parseArray(actualResult);
                            if (var1 == null) {
                                actualResult = null;
                            } else {
                                if (var1.size() == 1) {
                                    Object var2 = var1.get(0);
                                    actualResult = var2 == null ? null : var2.toString();
                                } else {
                                    actualResult = JSON.toJSONString(var1);
                                }
                            }
                        }
                    } else {
                        actualResult = JSON.toJSONString(resultList);
                    }
                }
                isPass = AssertUtil.asserts(actualResult, operator, exceptedResult);
                if (isPass) {
                    log.info("断言成功");
                    assertStatus = 0;
                } else {
                    log.warn("断言失败");
                    assertStatus = 1;
                }
            } catch (Exception e) {
                assertStatus = 2;
                log.error("断言失败, errorMsg={}", ExceptionUtil.msg(e));
                assertErrorMessage = e.getMessage();
            }
            assertLogDO.setActualResult(actualResult);
            assertLogDO.setStatus(assertStatus);
            statusList.add(assertStatus); // 将每次断言status都加入集合
            assertLogDO.setErrorMessage(assertErrorMessage);
            assertLogDO.setCreatedTime(new Date());
            assertLogService.saveAssertLog(assertLogDO);
        }


        ApiTestCaseExecuteLog updateStatus = new ApiTestCaseExecuteLog();
        updateStatus.setId(executedLogId);
        if (statusList.contains( 2)) {
            updateStatus.setStatus( 2);
        } else {
            if (!statusList.contains( 1)) {
                updateStatus.setStatus( 0);
            } else {
                updateStatus.setStatus(1);
            }
        }
        executeLogService.modifyExecuteLog(updateStatus);

        // 修改调用链
        ApiTestCaseExecuteLog updateChain = new ApiTestCaseExecuteLog();
        updateChain.setId(executedLogId);
        updateChain.setChain(JSON.toJSONString(redisUtil.stackGetAll(chainNo)));
        executeLogService.modifyExecuteLog(updateChain);

        // 返回用例执行断言后的状态
        return updateStatus.getStatus();
    }


    /**
     * 将测试套件中的header、params、form-data-encoded合并到请求信息中
     * @param suiteProperty 测试套件中的属性
     * @param kv 测试用例的属性
     * @return 合并后的属性
     */
    private HashMap mergeSuiteProperty(HashMap suiteProperty, String kv) {
        HashMap property = JSONObject.parseObject(kv, HashMap.class);
        if (suiteProperty != null) {
            if (property != null) {
                suiteProperty.putAll(property);
            }
            return suiteProperty;
        } else {
            return property;
        }
    }

    /**
     * 发送http请求
     * @param bodyType bodyType
     * @param methodEnum 请求方式枚举
     * @param url 请求url
     * @param headersMap headersMap
     * @param paramsMap paramsMap
     * @param formDataMap formDataMap
     * @param formDataEncodedMap formDataEncodedMap
     * @param raw raw
     * @param rawType raw类型
     * @return ResponseEntity
     */
    public ResponseEntity doRequest(Integer bodyType, HttpMethod methodEnum,
                                    String url, HashMap headersMap, HashMap paramsMap,
                                    HashMap formDataMap, HashMap formDataEncodedMap,
                                    String raw, String rawType)  {
        if (bodyType == 0) { //form-data
            return Request.requestPro(methodEnum, url, headersMap, paramsMap, formDataMap, MediaType.MULTIPART_FORM_DATA);
        } else if (bodyType == 1) { //x-www-form-encoded
            return Request.requestPro(methodEnum, url, headersMap, paramsMap, formDataEncodedMap, MediaType.APPLICATION_FORM_URLENCODED);
        } else if (bodyType == 2) { //raw
            if ("Text".equalsIgnoreCase(rawType)) {
                return Request.requestPro(methodEnum, url, headersMap, paramsMap, raw, MediaType.TEXT_PLAIN);
            } else if ("JSON".equalsIgnoreCase(rawType)) {
                return Request.requestPro(methodEnum, url, headersMap, paramsMap, raw, MediaType.APPLICATION_JSON);
            } else if ("HTML".equalsIgnoreCase(rawType)) {
                return Request.requestPro(methodEnum, url, headersMap, paramsMap, raw, MediaType.TEXT_HTML);
            } else if ("XML".equalsIgnoreCase(rawType)) {
                return Request.requestPro(methodEnum, url, headersMap, paramsMap, raw, MediaType.APPLICATION_XML);
            } else {
                throw new LeaseException( ResultCodeEnum.RAW_TYPE_NOT_ERROR);
            }
        } else if (bodyType == 9) { //none
            return Request.requestPro(methodEnum, url, headersMap, paramsMap, null);
        } else {
            throw new LeaseException( ResultCodeEnum.BODY_TYPE_NOT_ERRORO);
        }
    }

    /**
     * 用例请求成功时触发（此时还未执行断言）
     * @param responseCode 响应状态码
     * @param responseHeaders 响应头
     * @param responseBody 响应体
     * @param interfaceCaseId 接口测试用例编号
     * @param url 请求URL
     * @param desc 描述
     * @param method 请求方式
     * @param headersMap 请求headers
     * @param paramsMap 请求params
     * @param requestBody 请求body
     * @param rawHeaders 未解析依赖前的headers
     * @param rawParams 未解析依赖前的params
     * @param rawBody 未解析依赖前的body
     * @param runTime 执行耗时
     * @param executor 执行人
     * @param suiteLogNo 测试套件日志编号 主要为调用入口为测试套件时使用，否则传参null
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param isFailedRetry 是否为失败重跑的请求 0是1否
     * @param source 来源（0用例调试 1依赖调试 2运行整个测试套件 3测试套件单个用例调试 4依赖解析 5综合用例-前置用例）
     * @param rawType raw类型 "Text" "JSON" "HTML" "XML"
     * @param bodyType 请求体类型0form-data 1x-www-form-Encoded 2raw 9none
     * @param caseStatus 日志执行状态
     * @return 日志编号
     */
    private Integer onRequestSuccess(Integer responseCode, String responseHeaders, String responseBody,
                                     Integer interfaceCaseId, String url, String desc, Integer method,
                                     HashMap headersMap, HashMap paramsMap, String requestBody,
                                     String rawHeaders, String rawParams, String rawBody,
                                     long runTime, String executor, String suiteLogNo, String suiteLogDetailNo,
                                     Integer isFailedRetry, Integer source, String rawType, Integer bodyType, Integer caseStatus) {
        String temp = responseBody;
        if (!temp.equals("")) {
            try {
                JSONObject responseBodyObject = JSONObject.parseObject(temp);
                responseBody = JSON.toJSONString(responseBodyObject, JSONWriter.Feature.PrettyFormat,  JSONWriter.Feature.WriteMapNullValue);
            } catch (Exception e) {
                responseBody = temp;
            }
        }
        ApiTestCaseExecuteLog executeLogDO = new ApiTestCaseExecuteLog();
        executeLogDO.setCaseId(interfaceCaseId);
        executeLogDO.setCaseDesc(desc);
        executeLogDO.setCaseMethod(method);
        executeLogDO.setRequestHeaders(JSON.toJSONString(headersMap,  JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(headersMap,  JSONWriter.Feature.PrettyFormat));
        executeLogDO.setRequestParams(JSON.toJSONString(paramsMap,  JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(paramsMap,  JSONWriter.Feature.PrettyFormat));
        executeLogDO.setRequestBody(requestBody);
        executeLogDO.setRawRequestHeaders(rawHeaders);
        executeLogDO.setRawRequestParams(rawParams);
        executeLogDO.setRawRequestBody(rawBody);
        executeLogDO.setResponseCode(responseCode);
        executeLogDO.setResponseHeaders(responseHeaders);
        executeLogDO.setResponseBody(responseBody);
        executeLogDO.setRunTime(runTime);
        executeLogDO.setExecuter(executor);
        executeLogDO.setStatus(caseStatus);
        executeLogDO.setCreateTime(new Date());
        executeLogDO.setCaseUrl(url);
        executeLogDO.setSuiteLogNo(suiteLogNo);
        executeLogDO.setSuiteLogDetailNo(suiteLogDetailNo);
        executeLogDO.setIsFailedRetry(isFailedRetry);
        executeLogDO.setSource(source);
        executeLogDO.setRawType(rawType);
        executeLogDO.setBodyType(bodyType);
        ApiTestCaseExecuteLog executedLogDO = executeLogService.saveApiTestCaseExecuteLog(executeLogDO);
        Integer logId = executedLogDO.getId();
        log.info("case [{}] execution success, executeLog={}, executeLogId={}", interfaceCaseId, executedLogDO, logId);
        return logId;
    }

    /**
     * 用例请求失败时触发（此时还未执行断言）
     * @param interfaceCaseId 用例编号
     * @param url 完整的url
     * @param desc 用例描述
     * @param method 请求方式
     * @param headersMap 请求headers
     * @param paramsMap 请求params
     * @param requestBody 请求body
     * @param rawHeaders 未解析依赖前的headers
     * @param rawParams 未解析依赖前的params
     * @param rawBody 未解析依赖前的body
     * @param executor 执行人
     * @param exceptionMessage 异常信息
     * @param runTime 执行耗时
     * @param suiteLogNo 测试套件日志编号 主要为调用入口为测试套件时使用，否则传参null
     * @param suiteLogDetailNo suiteLogNo仅记录重跑和真正运行的，suiteLogDetailNo会包括用例所依赖的case
     * @param isFailedRetry 是否为失败重跑的请求 0是1否
     * @param source 来源（0用例调试 1依赖调试 2运行整个测试套件 3测试套件单个用例调试 4依赖解析 5综合用例-前置用例）
     * @param rawType raw类型 "Text" "JSON" "HTML" "XML"
     * @param bodyType 请求体类型0form-data 1x-www-form-Encoded 2raw 9none
     * @param chainNo 调用链路跟踪 将调用链信息序列化
     * @return 日志编号
     */
    private Integer onRequestError(Integer interfaceCaseId, String url, String desc, Integer method,
                                   HashMap headersMap, HashMap paramsMap, String requestBody,
                                   String rawHeaders, String rawParams, String rawBody,
                                   String executor, String exceptionMessage, long runTime,
                                   String suiteLogNo, String suiteLogDetailNo, Integer isFailedRetry,
                                   Integer source, String rawType, Integer bodyType, String chainNo) {
        // 写入对象
        ApiTestCaseExecuteLog apiTestCaseExecuteLog = new ApiTestCaseExecuteLog();
        ApiTestCaseExecuteLog executeLogDO = new ApiTestCaseExecuteLog();
        executeLogDO.setCaseId(interfaceCaseId);
        executeLogDO.setCaseDesc(desc);
        executeLogDO.setCaseMethod(method);
        executeLogDO.setRequestHeaders(JSON.toJSONString(headersMap, JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(headersMap, JSONWriter.Feature.PrettyFormat));
        executeLogDO.setRequestParams(JSON.toJSONString(paramsMap, JSONWriter.Feature.PrettyFormat).equals("null") ? "" : JSON.toJSONString(paramsMap, JSONWriter.Feature.PrettyFormat));
        executeLogDO.setRequestBody(requestBody);
        executeLogDO.setRawRequestHeaders(rawHeaders);
        executeLogDO.setRawRequestParams(rawParams);
        executeLogDO.setRawRequestBody(rawBody);
        executeLogDO.setResponseCode(null);
        executeLogDO.setResponseHeaders(null);
        executeLogDO.setResponseBody(null);
        executeLogDO.setExecuter(executor);
        executeLogDO.setStatus(2);
        executeLogDO.setCreateTime(new Date());
        executeLogDO.setErrorMessage(exceptionMessage);
        executeLogDO.setRunTime(runTime);
        executeLogDO.setCaseUrl(url);
        executeLogDO.setSuiteLogNo(suiteLogNo);
        executeLogDO.setSuiteLogDetailNo(suiteLogDetailNo);
        executeLogDO.setIsFailedRetry(isFailedRetry);
        executeLogDO.setSource(source);
        executeLogDO.setRawType(rawType);
        executeLogDO.setBodyType(bodyType);
        ApiTestCaseExecuteLog executeLog = executeLogService.saveApiTestCaseExecuteLog(executeLogDO);
        // 调用链信息序列化保存
        Integer executeLogId = executeLog.getId();

        ApiTestCaseExecuteLog updateChain = new ApiTestCaseExecuteLog();
        updateChain.setRunTime(runTime);
        updateChain.setId(executeLogId);
        updateChain.setChain(JSON.toJSONString(redisUtil.stackGetAll( chainNo)));
        executeLogService.modifyExecuteLog(updateChain);
        log.error("case [{}] execution error, executeLog={}, executeLogId={}", interfaceCaseId, executeLog, executeLogId);
        return executeLogId;
    }


    /**
     * 根据bodyType，确定执行日志中的rawBody
     * @param bodyType bodyType
     * @return 执行日志中记录的rawBody
     */
    private String logRawBody(Integer bodyType, String formData, String formDataEncoded, String raw) {
        if (bodyType == null){
            throw new LeaseException(ResultCodeEnum.BODY_TYPE_NULL_ERROR);
        }
        if (bodyType == 0) { //form-data
            return formData;
        } else if (bodyType == 1) { //x-www-form-encoded
            return formDataEncoded;
        } else if (bodyType == 2) { //raw
            return raw;
        } else if (bodyType == 9) { //none
            return null;
        } else {
            throw new LeaseException(ResultCodeEnum.BODY_TYPE_NOT_ERRORO);

        }
    }

    // 将前端包含name、value、checked、value的对象数组转成对象
    public String kvCast(String text) {
        try {
            JSONObject object = new JSONObject();
            JSONArray array = parseArray(text);
            if (array != null && !array.isEmpty()) {
                array.forEach(item -> {
                    String s = JSON.toJSONString(item);
                    JSONObject var1 = JSON.parseObject(s);
                    String name = var1.getString("name");
                    String value = var1.getString("value");
                    boolean checked = var1.getBooleanValue("checked");
                    if (checked) {
                        object.put(name, value);
                    }
                });
                return JSON.toJSONString(object);
            }
            return null;
        } catch (Exception e) {
            throw new LeaseException(ResultCodeEnum.WEB_API_PARAM_ERROR);
        }

    }


}
